#include "persistence.h"

Persistence::~Persistence()
{   
    if (wal_log_file_.is_open()){
        wal_log_file_.close();
    }
}

void Persistence::init(const std::string& path)
{ 
    wal_log_file_.open(path,std::ios::in|std::ios::out|std::ios::app);
    if(!wal_log_file_.is_open()){
        GlobalLogger->error("Failed to open wal log file,reason: {}",std::strerror(errno));
        throw std::runtime_error("Failed to open wal log file: "+path);
    }
}

uint64_t Persistence::increaseID(){
    increaseID_++;
    return increaseID_;
}
uint64_t Persistence::getID()const{
    return increaseID_;
}
void Persistence::writeWALLog(const std::string& operation_type,const rapidjson::Document& json_data,const std::string& version){
    uint64_t log_id=increaseID();
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    json_data.Accept(writer);

    wal_log_file_ << log_id << "|" << version << "|" << operation_type << "|" << buffer.GetString() << std::endl;

    if(wal_log_file_.fail()){
        GlobalLogger->error("An error occurred while writing the WAL log entry. Reason: {}", std::strerror(errno));
    }else{
        GlobalLogger->info("Wrote WAL log entry: log_id={}, version={}, operation_type={}, json_data_str={}", log_id, version, operation_type, buffer.GetString());
        wal_log_file_.flush();
    }
}

void Persistence::writeWALRawLog(uint64_t log_id, const std::string &operation_type, const std::string &raw_data, const std::string &version)
{
    wal_log_file_ << log_id << "|" << version << "|" << operation_type << "|" << raw_data << std::endl;

    if(wal_log_file_.fail()){
        GlobalLogger->error("An error occurred while writing the WAL log entry. Reason: {}", std::strerror(errno));
    }else{
        GlobalLogger->info("Wrote WAL log entry: log_id={}, version={}, operation_type={}, raw_data={}", log_id, version, operation_type, raw_data);
        wal_log_file_.flush();
    }
}

void Persistence::readNextWALLog(std::string *operation_type,rapidjson::Document* json_data){
    GlobalLogger->debug("Reading next WAL log entry");

    std::string line;
    if(std::getline(wal_log_file_, line)){
        std::istringstream iss(line);
        std::string log_id_str, version, json_data_str;

        std::getline(iss,log_id_str,'|');
        std::getline(iss,version,'|');
        std::getline(iss,*operation_type,'|');
        std::getline(iss,json_data_str,'|');

        uint64_t log_id=std::stoull(log_id_str);
        if(log_id>increaseID_){
            increaseID_=log_id;
        }

        if(log_id>lastSnapshotID){
            json_data->Parse(json_data_str.c_str());
            GlobalLogger->debug("read WAL log entry: log_id: {}, version: {}, operation_type: {}, json_data: {}",log_id_str,version,*operation_type,json_data_str);
            return;
        }else{
            GlobalLogger->debug("skip read WAL log entry: log_id: {}, version: {}, operation_type: {}, json_data: {}",log_id_str,version,*operation_type,json_data_str);
        }

        json_data->Parse(json_data_str.c_str());
        GlobalLogger->debug("Read WAL log entry: log_id={}, version={}, operation_type={}, json_data={}",log_id,version,*operation_type,json_data_str);
    }else{
        wal_log_file_.clear();
        GlobalLogger->debug("No more WAL log entry to read");
    }
}

void Persistence::takeSnapshot(ScalarStorage& scalarStorage){
    GlobalLogger->debug("Taking snapshot");
    lastSnapshotID=increaseID();
    std::string snapshotFolderPath="snapshots_";
    IndexFactory& indexFactory=IndexFactory::getGlobalIndexFactory();
    indexFactory.saveIndex(std::to_string(lastSnapshotID),scalarStorage);

    saveLastSnapshotID();
}
void Persistence::loadSnapshot(ScalarStorage& scalarStorage){
    GlobalLogger->debug("Loading snapshot");
    IndexFactory& indexFactory=IndexFactory::getGlobalIndexFactory();
    indexFactory.loadIndex("snapshots_",scalarStorage);
}
void Persistence::saveLastSnapshotID(){
    std::ofstream file("snapshots_MaxLogID");
    if(file.is_open()){
        file<<lastSnapshotID;
        file.close();
    }else{
        GlobalLogger->error("Failed to open file snapshots_MaxID for writing");

    }
}
void Persistence::loadLastSnapshotID(){
    std::ifstream file("snapshots_MaxLogID");
    if(file.is_open()){
        file>>lastSnapshotID;
        file.close();
    }else{
        GlobalLogger->error("Failed to open file snapshots_MaxID for reading");
    }

    GlobalLogger->debug("Loaded last snapshot ID: {}",lastSnapshotID);
}